/*//////////////////////////////////////////////////////////////////////////////
//
//                  INTEL CORPORATION PROPRIETARY INFORMATION
//     This software is supplied under the terms of a license agreement or
//     nondisclosure agreement with Intel Corporation and may not be copied
//     or disclosed except in accordance with the terms of that agreement.
//          Copyright(c) 2002-2008 Intel Corporation. All Rights Reserved.
//
*/

#include "umc_defs.h"

#if defined (UMC_ENABLE_AC3_AUDIO_DECODER)

#include "umc_ac3_decoder.h"
#include "ac3_dec.h"
#include "vm_debug.h"

namespace UMC {

/********************************************************************/

  AudioCodec *CreateAC3Decoder() { return (new AC3Decoder); }

  AC3Decoder::AC3Decoder()
  {
    state = NULL;
    stateMemId = 0;
  }

/********************************************************************/

  Status AC3Decoder::Init(BaseCodecParams* init)
  {
    AC3Status result;
    Ipp32s size = 0;

    result = ac3decInit(NULL, &size);

    if (AC3_OK != result)
      return StatusAC3_2_UMC(result);

    // checks or create memory allocator;
    if (BaseCodec::Init(init) != UMC_OK) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("Failed to create allocator!\n"));
      return UMC_ERR_ALLOC;
    }

    if (m_pMemoryAllocator->Alloc(&stateMemId, size, UMC_ALLOC_PERSISTENT) != UMC_OK) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("External allocation failed\n"));
      return UMC_ERR_ALLOC;
    }

    state = (AC3Dec *)m_pMemoryAllocator->Lock(stateMemId);
    if (!state) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("External Lock failed\n"));
      return UMC_ERR_ALLOC;
    }

    result = ac3decInit(state, &size);
    MemUnlock();
    if (AC3_OK == result) {
      if (NULL != init)
        SetParams(init);
      return UMC_OK;
    } else {
      return StatusAC3_2_UMC(result);
    }
  }

/********************************************************************/

  Status AC3Decoder::GetFrame(MediaData* in,
                              MediaData* out)
  {
    AC3Status result;
    Ipp32s nChannelOut, nDecodedBytes, SampleRate;
    Ipp32s outBufferSize;
    Status status;

    if (!in || !out)
      return UMC_ERR_NULL_PTR;

    outBufferSize = (Ipp32s)out->GetBufferSize() -
                    (Ipp32s)((Ipp8u *)out->GetDataPointer() -
                             (Ipp8u *)out->GetBufferPointer());

    status = MemLock();
    if (status != UMC_OK) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("MemLock failed\n"));
      return status;
    }

    if (state == NULL)
      return UMC_ERR_NOT_INITIALIZED;

    result = ac3decGetFrame((Ipp8u *)in->GetDataPointer(),
                            in->GetDataSize(), &nDecodedBytes,
                            (Ipp16s *)out->GetDataPointer(),
                            outBufferSize, state);

    ac3decGetNumChannelOut(&nChannelOut, state);

    if (AC3_BAD_STREAM == result) {
      if (outBufferSize < (Ipp32s)(nChannelOut*256*6*sizeof(Ipp16s))) {
        MemUnlock();
        return UMC_ERR_NOT_ENOUGH_BUFFER;
      }
    }

    in->MoveDataPointer(nDecodedBytes);
    ac3decGetSampleFrequency(&SampleRate, state);

    if (nDecodedBytes && (AC3_OK == result || AC3_BAD_STREAM == result)) {
      Ipp64f pts_start = in->GetTime();
      Ipp64f pts_end;

      if (pts_start == -1.0)
        pts_start = m_pts_prev;

      m_pts_prev = pts_end = pts_start + (256*6)/((Ipp32f)SampleRate);
      in->SetTime(pts_end);

      if (AC3_BAD_STREAM == result) {
        // fill with silent data
        memset(out->GetDataPointer(),0,nChannelOut*256*6*2);
      }

      out->SetDataSize(nChannelOut*256*6*2);
      out->SetTime(pts_start, pts_end);
    }

    if (AC3_OK == result) {
      AudioData* pAudio = DynamicCast<AudioData,MediaData>(out);

      if (pAudio) {
        pAudio->m_info.bitPerSample = 16;
        pAudio->m_info.bitrate = 0;

        pAudio->m_info.channels = nChannelOut;
        pAudio->m_info.sample_frequency = SampleRate;
        pAudio->m_info.stream_type = PCM_AUDIO;
      }
    }

    MemUnlock();
    return StatusAC3_2_UMC(result);
  }

/********************************************************************/

  Status AC3Decoder::SetParams(BaseCodecParams * params) {
    AC3DecoderParams *info = DynamicCast < AC3DecoderParams > (params);

    if (info) {
      Status status;

      status = MemLock();
      if (status != UMC_OK) {
        vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("MemLock failed\n"));
        return status;
      }

      if (state == NULL)
        return UMC_ERR_NOT_INITIALIZED;

      ac3decSetOutAcmod(info->out_acmod, state);
      ac3decSetOuLfeOn(info->outlfeon, state);
      ac3decSetDualMonoMode(info->dualmonomode, state);
      ac3decSetDrcScaleLow(info->drc_scaleLow, state);
      ac3decSetDrcScaleHigh(info->drc_scaleHigh, state);
      ac3decSetOutCompMod(info->out_compmod, state);
      ac3decSetKaraokeCapable(info->karaokeCapable, state);
      ac3decSetCrcMute(info->crc_mute, state);
      ac3decSetGainScale(info->gainScale, state);
    } else {
      return UMC_ERR_NULL_PTR;
    }
    return UMC_OK;
  }

/********************************************************************/

  AC3Decoder::~AC3Decoder()
  {
    Close();
  }

/********************************************************************/

  Status AC3Decoder::Close()
  {
    if (state == NULL)
      return UMC_OK;

    if (m_pMemoryAllocator == NULL) {
      return UMC_OK;
    }

    if (state) {
      m_pMemoryAllocator->Free(stateMemId);
      state = NULL;
    }
    BaseCodec::Close();
    state = NULL;
    return UMC_OK;
  }

/********************************************************************/

  Status AC3Decoder::Reset()
  {
    Status status;

    status = MemLock();
    if (status != UMC_OK) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("MemLock failed\n"));
      return status;
    }

    if (state == NULL)
      return UMC_ERR_NOT_INITIALIZED;

    ac3decReset(state);

    MemUnlock();
    return UMC_OK;
  }

/********************************************************************/

  Status AC3Decoder::GetInfo(BaseCodecParams* info)
  {
    Status status;

    if (!info)
      return UMC_ERR_NULL_PTR;

    info->m_SuggestedInputSize = 1920 * 2;
    info->m_SuggestedOutputSize = 6 * (256 * 6) * sizeof(Ipp16s);
    info->level = AC3_LEVEL_MAIN;
    info->profile = AC3_PROFILE_MAIN;

    AudioCodecParams *p_info =
      DynamicCast < AudioCodecParams, BaseCodecParams > (info);

    status = MemLock();
    if (status != UMC_OK) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("MemLock failed\n"));
      return status;
    }

    ac3decGetInfo(&params, state);

    if (!p_info)
      return UMC_OK;

    if (p_info) {
      p_info->m_info_out.channels = params.m_info_out.channels;
      p_info->m_info_in.channels = params.m_info_in.channels;
      p_info->m_info_in.stream_type = AC3_AUDIO;
      p_info->m_info_out.stream_type = PCM_AUDIO;

      if (params.is_valid) {
        p_info->m_info_in.bitPerSample = params.m_info_in.bitPerSample;
        p_info->m_info_out.bitPerSample = params.m_info_out.bitPerSample;

        p_info->m_info_in.bitrate      = params.m_info_in.bitrate;
        p_info->m_info_out.bitrate     = params.m_info_out.bitrate;

        p_info->m_info_in.sample_frequency = params.m_info_in.sample_frequency;
        p_info->m_info_out.sample_frequency = params.m_info_out.sample_frequency;

        p_info->m_frame_num = params.m_frame_num;

        p_info->m_info_in.bitrate = params.m_info_in.bitrate;
        p_info->m_info_out.bitrate = params.m_info_out.bitrate;

        p_info->m_info_in.channel_mask = 0;

        if (params.m_info_in.channel_mask & AC3_CHANNEL_FRONT_CENTER)
          p_info->m_info_in.channel_mask |= CHANNEL_FRONT_CENTER;

        if (params.m_info_in.channel_mask & AC3_CHANNEL_FRONT_LEFT)
          p_info->m_info_in.channel_mask |= CHANNEL_FRONT_LEFT;

        if (params.m_info_in.channel_mask & AC3_CHANNEL_FRONT_RIGHT)
          p_info->m_info_in.channel_mask |= CHANNEL_FRONT_RIGHT;

        if (params.m_info_in.channel_mask & AC3_CHANNEL_BACK_CENTER)
          p_info->m_info_in.channel_mask |= CHANNEL_BACK_CENTER;

        if (params.m_info_in.channel_mask & AC3_CHANNEL_BACK_LEFT)
          p_info->m_info_in.channel_mask |= CHANNEL_BACK_LEFT;

        if (params.m_info_in.channel_mask & AC3_CHANNEL_BACK_RIGHT)
          p_info->m_info_in.channel_mask |= CHANNEL_BACK_RIGHT;

        if (params.m_info_in.channel_mask & AC3_CHANNEL_LOW_FREQUENCY)
          p_info->m_info_in.channel_mask |= CHANNEL_LOW_FREQUENCY;

        p_info->m_info_out.channel_mask = 0;

        if (params.m_info_out.channel_mask & AC3_CHANNEL_FRONT_CENTER)
          p_info->m_info_out.channel_mask |= CHANNEL_FRONT_CENTER;

        if (params.m_info_out.channel_mask & AC3_CHANNEL_FRONT_LEFT)
          p_info->m_info_out.channel_mask |= CHANNEL_FRONT_LEFT;

        if (params.m_info_out.channel_mask & AC3_CHANNEL_FRONT_RIGHT)
          p_info->m_info_out.channel_mask |= CHANNEL_FRONT_RIGHT;

        if (params.m_info_out.channel_mask & AC3_CHANNEL_BACK_CENTER)
          p_info->m_info_out.channel_mask |= CHANNEL_BACK_CENTER;

        if (params.m_info_out.channel_mask & AC3_CHANNEL_BACK_LEFT)
          p_info->m_info_out.channel_mask |= CHANNEL_BACK_LEFT;

        if (params.m_info_out.channel_mask & AC3_CHANNEL_BACK_RIGHT)
          p_info->m_info_out.channel_mask |= CHANNEL_BACK_RIGHT;

        if (params.m_info_out.channel_mask & AC3_CHANNEL_LOW_FREQUENCY)
          p_info->m_info_out.channel_mask |= CHANNEL_LOW_FREQUENCY;
      }
    } else {
      MemUnlock();
      return UMC_WRN_INFO_NOT_READY;
    }
    MemUnlock();
    return UMC_OK;
  }

/********************************************************************/

  Status AC3Decoder::GetDuration(Ipp32f* p_duration)
  {
    Status status;

    if (!p_duration)
      return UMC_ERR_NULL_PTR;

    status = MemLock();
    if (status != UMC_OK) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("MemLock failed\n"));
      return status;
    }

    if (state == NULL)
        return UMC_ERR_NOT_INITIALIZED;

    ac3decGetDuration(p_duration, state);
    MemUnlock();
    return UMC_OK;
  }

/********************************************************************/

  Status AC3Decoder::StatusAC3_2_UMC(AC3Status st)
  {
    Status res;
    if (st == AC3_OK)
      res = UMC_OK;
    else if (st == AC3_NOT_ENOUGH_DATA)
      res = UMC_ERR_NOT_ENOUGH_DATA;
    else if (st == AC3_FLAGS_ERROR)
      res = UMC_ERR_INVALID_PARAMS;
    else if (st == AC3_ALLOC)
      res = UMC_ERR_ALLOC;
    else if (st == AC3_BAD_STREAM)
      res = UMC_ERR_INVALID_STREAM;
    else if (st == AC3_NULL_PTR)
      res = UMC_ERR_NULL_PTR;
    else if (st == AC3_NOT_FIND_SYNCWORD)
      res = UMC_ERR_SYNC;
    else if (st == AC3_NOT_ENOUGH_BUFFER)
      res = UMC_ERR_NOT_ENOUGH_BUFFER;
    else if (st == AC3_UNSUPPORTED)
      res = UMC_ERR_UNSUPPORTED;
    else
      res = UMC_ERR_UNSUPPORTED;

    return res;
  }

/****************************************************************************/

  Status AC3Decoder::MemLock() {
    AC3Dec *pOldState = state;

    /* AYA: 07 june 07 failed if this check delete */
    if( m_pMemoryAllocator == NULL ) {
      return UMC_OK;
    }

    state = (AC3Dec *)m_pMemoryAllocator->Lock(stateMemId);
    if(!state) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("External Lock failed\n"));
      return UMC_ERR_ALLOC;
    }
    if (state != pOldState) {
      ac3decUpdateMemMap(state, (Ipp8u *)state-(Ipp8u *)pOldState);
    }
    return UMC_OK;
  }

/****************************************************************************/

  Status AC3Decoder::MemUnlock() {
    if (stateMemId) {
      /* AYA: 07 june 07 failed if this check delete */
      if( m_pMemoryAllocator == NULL ) {
        return UMC_OK;
      }
      if (m_pMemoryAllocator->Unlock(stateMemId) != UMC_OK) {
        vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("External Unlock failed\n"));
        return UMC_ERR_ALLOC;
      }
    }
    return UMC_OK;
  }

/****************************************************************************/
};      // namespace UMC

/****************************************************************************/

#endif //UMC_ENABLE_AC3_AUDIO_DECODER


